package com.rsen.github.easyandroidanimations;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Point;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;

import java.util.ArrayList;
import java.util.List;

/**
 * This animation translates the view within its parent view and according to
 * the ArrayList<Point> provided by the user. The values of X and Y in each
 * Point must be in the range of 0-100. Note: The status bar and action bar are
 * not taken into consideration for the translation.
 * 
 * @author SiYao
 * 
 */
public class PathAnimation extends Animation implements Combinable {

	public static final int ANCHOR_CENTER = 0, ANCHOR_TOP_LEFT = 1,
			ANCHOR_TOP_RIGHT = 2, ANCHOR_BOTTOM_LEFT = 3,
			ANCHOR_BOTTOM_RIGHT = 4;

	ArrayList<Point> points;
	int anchorPoint;
	TimeInterpolator interpolator;
	long duration;
	AnimationListener listener;

	/**
	 * This animation translates the view within its parent view and according
	 * to the ArrayList<Point> provided by the user. The values of X and Y in
	 * each Point must be in the range of 0-100. Note: The status bar and action
	 * bar are not taken into consideration for the translation.
	 * 
	 * @param view
	 *            The view to be animated.
	 */
	public PathAnimation(View view) {
		this.view = view;
		points = null;
		anchorPoint = ANCHOR_CENTER;
		interpolator = new AccelerateDecelerateInterpolator();
		duration = DURATION_LONG;
		listener = null;
	}

	@Override
	public void animate() {
		getAnimatorSet().start();
	}

	@Override
	public AnimatorSet getAnimatorSet() {
		ViewGroup parentView = (ViewGroup) view.getParent(), rootView = (ViewGroup) view
				.getRootView();
		while (!parentView.equals(rootView)) {
			parentView.setClipChildren(false);
			parentView = (ViewGroup) parentView.getParent();
		}
		rootView.setClipChildren(false);

		AnimatorSet pathSet = new AnimatorSet();
		int numOfPoints = points.size();
		AnimatorSet[] pathAnimSetArray = new AnimatorSet[numOfPoints];
		List<Animator> pathAnimList = new ArrayList<Animator>();

		parentView = (ViewGroup) view.getParent();
		int parentWidth = parentView.getWidth(), parentHeight = parentView
				.getHeight(), viewWidth = view.getWidth(), viewHeight = view
				.getHeight();
		float posX, posY;
		for (int i = 0; i < numOfPoints; i++) {
			posX = (points.get(i).x / 100f * parentWidth);
			posY = (points.get(i).y / 100f * parentHeight);

			switch (anchorPoint) {
			case ANCHOR_CENTER:
				posX = posX - viewWidth / 2;
				posY = posY - viewHeight / 2;
				break;
			case ANCHOR_TOP_RIGHT:
				posX -= viewWidth;
				break;
			case ANCHOR_BOTTOM_LEFT:
				posY -= viewHeight;
				break;
			case ANCHOR_BOTTOM_RIGHT:
				posX = posX - viewWidth;
				posY = posY - viewHeight;
				break;
			default:
				break;
			}
			pathAnimSetArray[i] = new AnimatorSet();
			pathAnimSetArray[i].playTogether(
					ObjectAnimator.ofFloat(view, View.X, posX),
					ObjectAnimator.ofFloat(view, View.Y, posY));
			pathAnimList.add(pathAnimSetArray[i]);
		}

		pathSet.playSequentially(pathAnimList);
		pathSet.setInterpolator(interpolator);
		pathSet.setDuration(duration / numOfPoints);
		pathSet.addListener(new AnimatorListenerAdapter() {

			@Override
			public void onAnimationEnd(Animator animation) {
				if (getListener() != null) {
					getListener().onAnimationEnd(PathAnimation.this);
				}
			}
		});
		return pathSet;
	}

	/**
	 * @return The ArrayList<Point> for the view to translate to.
	 * @see java.util.ArrayList
	 */
	public ArrayList<Point> getPoints() {
		return points;
	}

	/**
	 * @param points
	 *            The ArrayList<Point> to set for the view to translate to.
	 * @return This object, allowing calls to methods in this class to be
	 *         chained.
	 * @see java.util.ArrayList
	 */
	public PathAnimation setPoints(ArrayList<Point> points) {
		this.points = points;
		return this;
	}

	/**
	 * The available anchor points at which to translate the view are
	 * <code>ANCHOR_CENTER</code>, <code>ANCHOR_TOP_LEFT</code>,
	 * <code>ANCHOR_TOP_RIGHT</code>, <code>ANCHOR_BOTTOM_LEFT</code> and
	 * <code>ANCHOR_BOTTOM_RIGHT</code>.
	 * 
	 * @return The anchor point at which to translate the view.
	 */
	public int getAnchorPoint() {
		return anchorPoint;
	}

	/**
	 * The available anchor points at which to translate the view are
	 * <code>ANCHOR_CENTER</code>, <code>ANCHOR_TOP_LEFT</code>,
	 * <code>ANCHOR_TOP_RIGHT</code>, <code>ANCHOR_BOTTOM_LEFT</code> and
	 * <code>ANCHOR_BOTTOM_RIGHT</code>.
	 * 
	 * @param anchorPoint
	 *            The anchor point to set at which to translate the view.
	 * @return This object, allowing calls to methods in this class to be
	 *         chained.
	 */
	public PathAnimation setAnchorPoint(int anchorPoint) {
		this.anchorPoint = anchorPoint;
		return this;
	}

	/**
	 * @return The interpolator of the entire animation.
	 */
	public TimeInterpolator getInterpolator() {
		return interpolator;
	}

	/**
	 * @param interpolator
	 *            The interpolator of the entire animation to set.
	 */
	public PathAnimation setInterpolator(TimeInterpolator interpolator) {
		this.interpolator = interpolator;
		return this;
	}

	/**
	 * @return The duration of the entire animation.
	 */
	public long getDuration() {
		return duration;
	}

	/**
	 * @param duration
	 *            The duration of the entire animation to set.
	 * @return This object, allowing calls to methods in this class to be
	 *         chained.
	 */
	public PathAnimation setDuration(long duration) {
		this.duration = duration;
		return this;
	}

	/**
	 * @return The listener for the end of the animation.
	 */
	public AnimationListener getListener() {
		return listener;
	}

	/**
	 * @param listener
	 *            The listener to set for the end of the animation.
	 * @return This object, allowing calls to methods in this class to be
	 *         chained.
	 */
	public PathAnimation setListener(AnimationListener listener) {
		this.listener = listener;
		return this;
	}
}
